---
title: 内存管理基础
---

内存管理是 C++ 编程的关键环节，它提供了对内存如何分配和释放的精细控制。这与 JavaScript 的自动垃圾回收机制有显著不同。

## 栈内存 vs. 堆内存

在 C++ 中，内存主要在两个区域进行管理：栈和堆。

### 栈内存
*   **自动分配：** 用于局部变量、函数参数和返回地址。
*   **后进先出 (LIFO)：** 内存以严格的顺序分配和释放。
*   **快速访问：** 分配和释放速度非常快。
*   **大小有限：** 通常比堆内存小。
*   **生命周期：** 栈上的变量在其超出作用域时会自动释放。

<UniversalEditor title="栈内存示例" compare={true}>
```javascript !! js
// JavaScript: 原始类型和函数调用的栈行为
function calculateSum(a, b) {
  let result = a + b; // 'result' 在概念上位于 calculateSum 的栈帧上
  return result;
}

let x = 10; // 'x' 在栈上
let y = 20; // 'y' 在栈上
let sum = calculateSum(x, y); // 函数调用添加一个栈帧
```

```cpp !! cpp
// C++: 栈内存
#include <iostream>

int calculateSum(int a, int b) {
  int result = a + b; // 'result' 在栈上分配
  return result;
} // 'result' 在 calculateSum 返回时释放

int main() {
  int x = 10; // 'x' 在栈上分配
  int y = 20; // 'y' 在栈上分配
  int sum = calculateSum(x, y); // calculateSum 添加一个栈帧
  // std::cout << "Sum: " << sum << std::endl;
  return 0;
} // 'x'、'y'、'sum' 在 main 返回时释放
```
</UniversalEditor>

### 堆内存
*   **动态分配：** 用于在编译时大小未知或生命周期超出函数作用域的数据。
*   **手动管理：** 程序员必须使用 `new` 和 `delete`（或 `malloc`/`free`）明确地分配和释放内存。
*   **较慢访问：** 分配和释放速度比栈内存慢。
*   **较大大小：** 通常比栈内存大得多。
*   **生命周期：** 内存会一直存在，直到明确释放或程序终止。

<UniversalEditor title="堆内存示例" compare={true}>
```javascript !! js
// JavaScript: 对象在堆上分配
let obj = { name: "Alice" }; // 'obj' 引用在栈上，对象数据在堆上

// 当 'obj' 不再被引用时，垃圾回收器会回收内存
obj = null; 
```

```cpp !! cpp
// C++: 堆内存
#include <iostream>

int main() {
  int* dynamicInt = new int; // 在堆上分配一个整数
  *dynamicInt = 100;
  // std::cout << "Dynamic Int: " << *dynamicInt << std::endl;

  delete dynamicInt; // 释放内存
  dynamicInt = nullptr; // 释放后将指针设置为 nullptr 是个好习惯

  // 在堆上分配一个数组
  int* dynamicArray = new int[5];
  for (int i = 0; i < 5; ++i) {
    dynamicArray[i] = i * 10;
  }
  // 数组需要使用 delete[] 释放
  delete[] dynamicArray;
  dynamicArray = nullptr;

  return 0;
}
```
</UniversalEditor>

## 变量生命周期

*   **自动 (栈) 变量：** 在进入其作用域时创建，并在其作用域退出时销毁。
*   **动态 (堆) 变量：** 使用 `new` 创建，并持续存在直到明确地 `delete`。
*   **静态变量：** 在程序启动时分配一次，并在程序的整个执行期间持续存在。
*   **全局变量：** 类似于静态变量，在程序启动时分配，并在程序的生命周期内持续存在。

## 内存布局基础

典型的 C++ 程序内存布局包括：
*   **文本/代码段：** 存储可执行指令。
*   **数据段：** 存储全局和静态变量（已初始化和未初始化）。
*   **堆：** 动态分配的内存。
*   **栈：** 局部变量和函数调用信息。

## 与 JavaScript 内存模型的比较

JavaScript 使用**垃圾回收器 (GC)** 自动管理内存。当一个对象不再可达（不再被程序的任何部分引用）时，GC 会自动回收内存。这使开发人员无需手动内存管理，减少了内存泄漏和悬空指针等常见错误。

C++ 则需要**手动内存管理**。开发人员负责在需要时分配内存，并在不再需要时释放内存。未能释放内存会导致**内存泄漏**，而尝试访问已释放的内存会导致**悬空指针**或**使用后释放**错误。

## 内存泄漏概念

**内存泄漏**发生在程序在堆上分配内存，但在不再需要时未能释放它。这会导致可用内存逐渐消耗，可能导致程序甚至整个系统变慢或崩溃。

**C++ 中内存泄漏的常见原因：**
*   忘记 `delete` 使用 `new` 分配的内存。
*   丢失动态分配内存的指针。
*   在错误路径或异常情况下错误地处理内存。

## 基本内存管理原则

1.  **需要时分配，完成时释放：** 每个 `new` 都必须有对应的 `delete`（或 `new[]` 与 `delete[]`）。
2.  **所有权：** 明确定义代码的哪个部分负责管理一段内存。
3.  **RAII (资源获取即初始化)：** 一种 C++ 编程惯用语，将资源（如内存）的生命周期与对象的生命周期绑定。当对象超出作用域时，其析构函数会自动释放资源。智能指针是 RAII 的主要范例。

## 调试内存问题

调试 C++ 中的内存问题可能具有挑战性。常用的工具包括：
*   **Valgrind (Linux/macOS)：** 一个强大的内存调试器，可以检测内存泄漏、使用后释放错误和其他内存相关问题。
*   **AddressSanitizer (Clang/GCC)：** 一个集成到编译器中的快速内存错误检测器。
*   **Visual Studio 诊断工具 (Windows)：** Visual Studio 中用于内存分析和泄漏检测的内置工具。

---

### 练习题：
1.  解释 C++ 中栈和堆内存之间的关键差异。何时会选择在堆而不是栈上分配内存？
2.  什么是内存泄漏，它在 C++ 中如何发生？JavaScript 的内存管理方法如何缓解这个问题？
3.  描述 RAII 原则以及它如何帮助 C++ 内存管理。

### 项目构想：
*   编写一个 C++ 程序，模拟一个简单的链表。实现添加节点、移除节点和打印列表的函数。密切注意内存分配和释放，以避免内存泄漏。明确使用 `new` 和 `delete`。
